﻿using NumSharp;
using NumSharp.Utilities;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
using System.Windows.Media;
using System.Windows.Shapes;

namespace NIDViewer;

public class ThermalTune
{
    public double QFactor { get; set; } = 0.0;
    public double PeakFreq { get; set; } = 0.0;
    public string PeakFreqUnit { get; set; } = "";
    public double PeakAmp { get; set; } = 0.0;
    public string PeakAmpUnit { get; set; } = "";
    public double SpringConst { get; set; } = 0.0;
    public string SpringConstUnit { get; set; } = "";
}

public class ImageData
{
    public Dictionary<string, NDArray> Forward { get; set; }
    public Dictionary<string, NDArray> Backward { get; set; }
    public Dictionary<string, NDArray> Forward2nd { get; set; }
    public Dictionary<string, NDArray> Backward2nd { get; set; }
}

public class SpecData
{
    public NDArray Forward { get; set; }
    public NDArray Backward { get; set; }
    public NDArray ForwardPause { get; set; }
    public NDArray BackwardPause { get; set; }
}

public class SpectrumData
{
    public Dictionary<string, NDArray> FFT { get; set; }
    public Dictionary<string, NDArray> Fit { get; set; }
    public Dictionary<string, NDArray> Sweep { get; set; }
    public Dictionary<string, NDArray> SweepSHO { get; set; }
}

public class NIDData
{
    public ImageData Image { get; set; } = new ImageData();
    public SpecData Spectroscopy { get; set; } = new SpecData();
    public SpectrumData Spectrum { get; set; } = new SpectrumData();
}

public class NIDDataInfo
{
    public Dictionary<string, string> SpecPosMap { get; set; } = new Dictionary<string, string>();
    public Dictionary<string, string> ImageDimInfo { get; set; } = new Dictionary<string, string>();
    public Dictionary<string, string> ImageOffset { get; set; } = new Dictionary<string, string>();
}

public class NIDHeaderInfo
{
    public Dictionary<string, string> Sections { get; set; } = new Dictionary<string, string>();
    public ThermalTune ThermalTune { get; set; } = new ThermalTune();
    public Dictionary<string, string> Cantilever { get; set; } = new Dictionary<string, string>();
}

public class NIDFileReader
{
    private ThermalTune? _ThermalTune;
    private string _filename;
    private FileStream? _FileStream;
    private StreamReader? _StreamReader;
    private Dictionary<string, string> _Cantilever;
    private Dictionary<string, List<object>> _Parm;
    private readonly List<string> _DataNames;
    private readonly List<int> _DataTypes;

    private ConfigParser _Header;
    private NIDDataInfo _Info;
    private ImageData _Image;
    private SpecData _Spec;
    private SpectrumData _Spectrum;

    public List<object> this[string key]
    {
        get => _Parm[key];
    }

    public bool ContainsKey(string key)
    {
        return _Parm.ContainsKey(key);
    }

    public NIDFileReader(string filename = "")
    {
        InitResult();
        _filename = filename;
        _DataNames = new List<string> { "Spec forward", "Spec backward", "Spec fwd pause", "Spec bwd pause", "Scan forward", "Scan backward", "2nd scan forward", "2nd scan backward", "Frequency sweep", "Frequency sweep SHO", "Spectrum FFT", "Spectrum Fit" };
        _DataTypes = new List<int> { 1, 1, 1, 1, 0, 0, 0, 0, 2, 2, 2, 2 };
    }

    private void InitResult()
    {
        _Header = new ConfigParser();
        _Info = new NIDDataInfo();
        _Image = new ImageData();
        _Spec = new SpecData();
        _Spectrum = new SpectrumData();
    }

    public (ConfigParser configParser, NIDDataInfo nidDataInfo, ImageData imageData, SpecData specData, SpectrumData spectrumData) GetData(string filename = "")
    {
        if (!string.IsNullOrEmpty(filename))
        {
            _filename = filename;
        }
        ReadHeader(_filename);
        ReadData(_filename);
        _FileStream?.Close();
        _FileStream = null;
        return (_Header, _Info, _Image, _Spec, _Spectrum);
    }

    /// <summary>
    /// 读取头部信息
    /// </summary>
    /// <param name="filename"></param>
    private void ReadHeader(string filename)
    {
        if (filename != null)
        {
            _FileStream ??= new FileStream(filename, FileMode.Open, FileAccess.Read);
            _StreamReader ??= new StreamReader(_FileStream);

            string dataHeaderPart = _StreamReader.ReadToEnd().Split(new[] { "#!" }, StringSplitOptions.None)[0];
            _Header = new ConfigParser();
            _Header.ReadString(dataHeaderPart);

            _Parm = new Dictionary<string, List<object>>();
            var groupNames = new List<string>();
            var dataSet = new List<string>();
            var ds = "DataSet";
            var grCnt = int.Parse(_Header[ds]["GroupCount"]);
            for (var gr = 0; gr < grCnt; gr++)
            {
                groupNames.Append(_Header[ds][$"Gr{gr}-Name"]);
                var gc = int.Parse(_Header[ds][$"Gr{gr}-Count"]);
                for (var g = 0; g < gc; g++)
                {
                    var grKey = $"Gr{gr}-Ch{g}";
                    if (_Header[ds].ContainsKey(grKey))
                    {
                        dataSet.Add(_Header[ds][grKey]);
                    }
                }
            }

            var terms = new List<string> { "Frame", "Points", "Lines", "SaveBits", "Dim0Min", "Dim0Range", "Dim0Unit", "Dim1Min", "Dim1Range", "Dim1Unit", "Dim2Min", "Dim2Range", "Dim2Unit", "Dim2Name" };
            var types = new List<int> { 0, 1, 1, 1, 2, 2, 0, 2, 2, 0, 2, 2, 0, 0 };

            List<Func<object, object>> typ = new()
            {
                x => x!=null?x.ToString(): "",   // 转换为字符串  
                x => Convert.ToInt32(x),   // 转换为整数  
                x => Convert.ToDouble(x)    // 转换为浮点数  
            };
            for (var i = 0; i < terms.Count; i++)
            {
                var term = terms[i];
                foreach (var data in dataSet)
                {
                    var h = _Header[data];
                    if (h.ContainsKey(term))
                    {
                        if (!_Parm.ContainsKey(term))
                        {
                            _Parm.Add(term, new List<object>());
                        }
                        _Parm[term].Add(typ[types[i]](h[term]));
                    }
                }
            }
            var dataPoints = new List<List<int>>();
            foreach (var data in dataSet)
            {
                var h = _Header[data];
                var linePoints = new List<int>();
                if (h.ContainsKey("LineDim0Min"))
                {
                    foreach (var dic in h)
                    {
                        if (Regex.IsMatch(dic.Key, @"^LineDim\d*Points$"))
                        {
                            linePoints.Add(int.Parse(dic.Value));
                        }
                    }
                }
                dataPoints.Add(linePoints);
            }
            if (!_Parm.ContainsKey("LinePoints"))
            {
                _Parm["LinePoints"] = dataPoints.ConvertAll(x => x as object);
            }
            _Cantilever = new Dictionary<string, string>();
            var key = "DataSet\\Calibration\\Cantilever";
            if (_Header.ContainsKey(key))
            {
                _Cantilever = _Header[key];
            }

            key = "DataSet\\Calibration\\Scanhead";
            if (_Header.ContainsKey(key))
            {
                var deflection_input = _Header[key]["In5"];
                var sensitivity = float.Parse(deflection_input.Split(',')[4]) / 10.0;
                _Cantilever["Sensitivity"] = sensitivity.ToString();
            }
            var spec_map = new List<string>();
            key = "DataSet\\SpecInfos";
            if (_Header.ContainsKey(key))
            {
                var n = int.Parse(_Header[key]["SubSectionCount"]);
                var secNames = new List<string>();
                for (int i = 0; i < n; i++)
                {
                    secNames.Add(_Header[key][$"SubSection{i}"]);
                }
                var specMode = _Header[key + '\\' + secNames[0]]["SpecMode"];
                var count = int.Parse(_Header[key + '\\' + secNames[1]]["Count"]);
                for (var i = 0; i < count; i++)
                {
                    spec_map.Add(_Header[key + '\\' + secNames[1]][specMode.Take(3) + i.ToString()]);
                }
            }

            var image_center = new List<double>();
            key = "DataSet\\Parameters\\Imaging";
            if (_Header.ContainsKey(key))
            {
                var ret = GetValues(key, "ScanOffset");
                if (ret.Item1 is double[] array)
                {
                    image_center.AddRange(array);
                }
            }
            _ThermalTune = null;
            key = "DataSet-Info";
            if (_Header.ContainsKey(key))
            {
                var data_set_info = _Header[key];
                if (data_set_info.ContainsKey("-- Thermal Tuning --"))
                {
                    _ThermalTune = new ThermalTune
                    {
                        QFactor = double.Parse(data_set_info["Q Factor:"])
                    };

                    var peak_freq = data_set_info["Frequency:"];
                    var gr = Regex.Match(peak_freq, @"(\d+\.\d+)(\S*)").Groups;
                    _ThermalTune.PeakFreq = double.Parse(gr[0].Value);
                    _ThermalTune.PeakFreqUnit = gr[1].Value;

                    var spring_c = data_set_info["Spring Constant:"];
                    gr = Regex.Match(spring_c, @"(\d+\.\d+)\s(\S*)").Groups;
                    _ThermalTune.SpringConst = double.Parse(gr[0].Value);
                    _ThermalTune.SpringConstUnit = gr[1].Value;

                    var peak_amp = data_set_info["Peak Value:"];
                    gr = Regex.Match(peak_amp, @"(\d+\.\d+)(\S*)").Groups;
                    _ThermalTune.PeakAmp = double.Parse(gr[0].Value);
                    _ThermalTune.PeakAmpUnit = gr[1].Value;
                }
            }
        }
    }

    private void ReadData(string filename)
    {
        if (filename != null)
        {
            _FileStream ??= new FileStream(filename, FileMode.Open, FileAccess.Read);
            _StreamReader ??= new StreamReader(_FileStream);

            if (_Parm != null)
            {
                var requiredSize = 0;
                List<int> points = _Parm["Points"].Select(x => Convert.ToInt32(x)).ToList();
                List<int> lines = _Parm["Lines"].Select(x => Convert.ToInt32(x)).ToList();
                NDArray pointArray = np.array(points.ToArray());
                NDArray lineArray = np.array(lines.ToArray());
                List<int> saveBits = _Parm["SaveBits"].Select(x => Convert.ToInt32(x)).ToList();
                for (int i = 0; i < points.Count; i++)
                {
                    requiredSize += (pointArray[i] * lineArray[i] * saveBits[i]) / 8;
                }

                Type dt;
                if (Convert.ToInt32(_Parm["SaveBits"][0]) == 16)
                {
                    dt = np.int16;
                }
                if (Convert.ToInt32(_Parm["SaveBits"][0]) == 32)
                {
                    dt = np.int32;
                }
                else
                {
                    throw new InvalidOperationException("Unsupported SaveBits value.");
                }
                var q = Math.Pow(2, Convert.ToDouble(_Parm["SaveBits"][0]));
                var z0 = q / 2;
                _FileStream.Seek(-requiredSize, SeekOrigin.End);
                // 创建字节数组并读取文件内容  
                byte[] byteData = new byte[requiredSize];
                _FileStream.Read(byteData, 0, requiredSize);
                // 计算每个元素的大小  
                int elementSize = Marshal.SizeOf(dt); // 获取类型大小
                // 计算元素个数  
                int elementCount = requiredSize / elementSize;
                // 将字节数组转换为对应类型的数组  
                Array dataArray = Array.CreateInstance(dt, elementCount);
                Buffer.BlockCopy(byteData, 0, dataArray, 0, requiredSize);
                var dataInArray = new NDArray(dataArray);
                // 转换为浮点数并进行缩放  
                dataInArray = dataInArray.astype(typeof(double));
                dataInArray = (dataInArray + z0) / q;

                // 计算累积和以分割数据  
                int[] cumulativeSum = new int[points.Count];
                cumulativeSum[0] = pointArray[0] * lineArray[0];
                for (int i = 1; i < points.Count; i++)
                {
                    cumulativeSum[i] = cumulativeSum[i - 1] + pointArray[i] * lineArray[i];
                }
                // 根据累积和进行数据分割  
                NDArray[] dataIn = new NDArray[cumulativeSum.Length];
                int startIndex = 0;
                for (int i = 0; i < cumulativeSum.Length; i++)
                {
                    int length = (i == 0) ? cumulativeSum[i] : cumulativeSum[i] - cumulativeSum[i - 1];
                    dataIn[i] = dataInArray[$"{startIndex}:{startIndex + length}"];
                    startIndex += length;
                }
                dataIn.RemoveAt(dataIn.Length - 1);

                List<NDArray> datas = new();
                List<double> zrans = _Parm["Dim2Range"].Select(x => Convert.ToDouble(x)).ToList();
                List<double> zmins = _Parm["Dim2Min"].Select(x => Convert.ToDouble(x)).ToList();
                NDArray zranArray = np.array(zrans.ToArray());
                NDArray zminArray = np.array(zmins.ToArray());
                try
                {
                    // 重塑和重新缩放数据  
                    for (int i = 0; i < dataIn.Length; i++)
                    {
                        NDArray pts = pointArray[i];
                        NDArray lns = lineArray[i];
                        NDArray zran = zranArray[i].astype(np.float32);
                        NDArray zmin = zminArray[i].astype(np.float32);

                        var demo11 = dataIn[i].reshape(lns, pts).astype(np.float32);
                        Debug.WriteLine($"重塑和重新缩放数据 {zran}");
                        var demo12 = np.multiply(demo11, zran);
                        var demo13 = np.add(demo12, zran);
                        //datas.Add(np.add(np.multiply(dataIn[i].reshape(lns, pts), zran), zmin));
                        datas.Add(demo13);
                    }
                }
                catch (Exception ex)
                {
                    Debug.WriteLine($"重塑和重新缩放数据 ERR:{ex.Message}");
                    return;
                }

                List<NDArray> dataCrop = new();
                var linePoints = _Parm["LinePoints"];
                for (int i = 0; i < linePoints.Count; i++)
                {
                    var dSet = datas[i];
                    if (linePoints[i] is List<int> linePoint && linePoint.Count > 0)
                    {
                        var temp = new List<NDArray>();
                        for (int j = 0; j < linePoint.Count; j++)
                        {
                            var n = linePoint[j];
                            temp.Add(dSet[j][$":{n}"]);
                        }
                        dataCrop.Add(np.array(temp.ToArray()));
                    }
                    else
                    {
                        dataCrop.Add(datas[i]);
                    }
                }

                // 查找框架索引  
                List<int> idx = _Parm["Frame"].Select((frame, index) => new { frame, index }).Where(item => _DataNames.Contains(item.frame.ToString())).Select(item => item.index).ToList();
                // 只选择批准列表中的数据  
                var selectDatas = idx.Select(index => dataCrop[index]).ToList();
                var frames = idx.Select(index => _Parm["Frame"][index]).ToList();
                var channel = idx.Select(index => _Parm["Dim2Name"][index]).ToList();

                // 将数据分类到结构中  
                var output = new Dictionary<string, Dictionary<string, NDArray>>();
                for (var i = 0; i < frames.Count; i++)
                {
                    var frame = frames[i] as string;
                    var chan = channel[i] as string;
                    if (!output.ContainsKey(frame))
                    {
                        output[frame] = new Dictionary<string, NDArray>();
                    }
                    output[frame][chan] = np.array(selectDatas[i]).astype(np.float64);
                }

                var spec = new SpecData();
                var spectrum = new SpectrumData();

                foreach (var item in output)
                {
                    switch (_DataTypes[_DataNames.IndexOf(item.Key)])
                    {
                        case 0:
                            switch (item.Key)
                            {
                                case "Scan forward":
                                    _Image.Forward = item.Value;
                                    break;
                                case "Scan backward":
                                    _Image.Backward = item.Value;
                                    break;
                                case "2nd scan forward":
                                    _Image.Forward2nd = item.Value;
                                    break;
                                case "2nd scan backward":
                                    _Image.Backward2nd = item.Value;
                                    break;
                            }
                            break;
                        case 1:
                            switch (item.Key)
                            {
                                case "Spec forward":
                                    _Image.Forward = item.Value;
                                    break;
                                case "Spec backward":
                                    _Image.Backward = item.Value;
                                    break;
                                case "Spec fwd pause":
                                    _Image.Forward2nd = item.Value;
                                    break;
                                case "Spec bwd pause":
                                    _Image.Backward2nd = item.Value;
                                    break;
                            }
                            break;
                        case 2:
                            switch (item.Key)
                            {
                                case "Spectrum FFT":
                                    spectrum.FFT = item.Value;
                                    break;
                                case "Spectrum Fit":
                                    spectrum.Fit = item.Value;
                                    break;
                                case "Frequency sweep":
                                    spectrum.Sweep = item.Value;
                                    break;
                                case "Frequency sweep SHO":
                                    spectrum.SweepSHO = item.Value;
                                    break;
                            }
                            break;
                    }
                }
            }
        }
    }

    private (object?, object?) GetValues(string key, string param)
    {
        string valueString = _Header[key][param];
        var match = Regex.Match(valueString, @"(\S)\[(.*)\]\*\[(.*)\]");
        var groups = match.Groups;
        string type = groups[1].Value;
        string value = groups[2].Value;
        string unit = groups[3].Value;
        switch (type)
        {
            case "D":
                return (double.Parse(value), unit);
            case "B":
                return (bool.Parse(value), unit);
            case "L":
                return (int.Parse(value), unit);
            case "V":
                var units = new List<string>();
                return (value.Split(',').Select(v => double.Parse(v, CultureInfo.InvariantCulture)).ToArray(), unit.Split(',').Select(v => v.ToString()).ToArray());
        }
        return (null, null);
    }

    private NDArray ConcatenateNDArrays(List<NDArray> ndArrays)
    {
        return np.concatenate(ndArrays.ToArray(), 0);
    }

    public NDArray? GetMatrixData(DataChannelTypes channel)
    {
        NDArray? matrix = null;
        switch (channel)
        {
            case DataChannelTypes.Amplitude_Forward:
                matrix = _Image.Forward["Amplitude"];
                break;
            case DataChannelTypes.Z_Axis_Forward:
                matrix = _Image.Forward["Z-Axis"];
                break;
            case DataChannelTypes.Phase_Forward:
                matrix = _Image.Forward["Phase"];
                break;
            case DataChannelTypes.Z_AxisSensor_Forward:
                matrix = _Image.Forward["Z-Axis Sensor"];
                break;
            case DataChannelTypes.Amplitude_Backward:
                matrix = _Image.Backward["Amplitude"];
                break;
            case DataChannelTypes.Z_Axis_Backward:
                matrix = _Image.Backward["Z-Axis"];
                break;
            case DataChannelTypes.Phase_Backward:
                matrix = _Image.Backward["Phase"];
                break;
            case DataChannelTypes.Z_AxisSensor_Backward:
                matrix = _Image.Backward["Z-Axis Sensor"];
                break;
        }
        return matrix;
    }
}